package com.atguigu.gmall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.atguigu.gmall.activity.client.ActivityFeignClient;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.service.RabbitService;
import com.atguigu.gmall.common.util.HttpClientUtil;
import com.atguigu.gmall.model.activity.ActivityRule;
import com.atguigu.gmall.model.activity.CouponInfo;
import com.atguigu.gmall.model.enums.OrderStatus;
import com.atguigu.gmall.model.enums.ProcessStatus;
import com.atguigu.gmall.model.order.*;
import com.atguigu.gmall.order.mapper.*;
import com.atguigu.gmall.order.service.OrderService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;

/**
 * @author mqx
 * @date 2021-3-1 10:50:43
 */
@Service
public class OrderServiceImpl extends ServiceImpl<OrderInfoMapper,OrderInfo> implements OrderService {

    @Autowired
    private OrderInfoMapper orderInfoMapper;

    @Autowired
    private OrderDetailMapper orderDetailMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private RabbitService rabbitService;

    @Autowired
    private OrderDetailActivityMapper orderDetailActivityMapper;

    @Autowired
    private OrderDetailCouponMapper orderDetailCouponMapper;

    @Autowired
    private OrderStatusLogMapper orderStatusLogMapper;

    @Autowired
    private ActivityFeignClient activityFeignClient;

    @Value("${ware.url}")
    private String wareUrl;  // wareUrl=http://localhost:9001
    @Override
    @Transactional(rollbackFor = Exception.class) // 做事务处理
    public Long saveOrderInfo(OrderInfo orderInfo) {
        /*
        1.  看用户前端传递的数据都有哪些
        2.  将没有传递的数据，进行赋值。
        3.  数据保存 order_info,order_detail
         */
        //  total_amount,order_status,user_id,out_trade_no,trade_body,
        //  create_time,expire_time,process_status
        //  计算总金额 ,因为在传递参数数据的时候 orderDetailList 是有数据的，会自动封装。
        orderInfo.sumTotalAmount();
        orderInfo.setOrderStatus(OrderStatus.UNPAID.name());
        //  user_id 在控制器中获取即可！*****
        //  第三方交易编号:在支付的时候使用 不能重复
        String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
        orderInfo.setOutTradeNo(outTradeNo);
        //  给一个固定的字符串,当前订单描述
        orderInfo.setTradeBody("过年了，买点衣服");
        //  另一种写法：你可以给每个商品的skuName ! 注意的是：长度是200， 不能超过整个数。
        orderInfo.setCreateTime(new Date());
        //  过期时间：给24小时
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DATE,1);
        orderInfo.setExpireTime(calendar.getTime());
        //  进度状态
        orderInfo.setProcessStatus(ProcessStatus.UNPAID.name());
        //  活动+优惠券 新增部分
        orderInfo.setFeightFee(new BigDecimal(0));
        orderInfo.setOperateTime(new Date());

        //  获取到促销金额
        BigDecimal activityReduceAmount = orderInfo.getActivityReduceAmount(orderInfo);
        orderInfo.setActivityReduceAmount(activityReduceAmount);

        //  计算购物项分摊的优惠减少金额，按比例分摊，退款时按实际支付金额退款
        Map<String, BigDecimal> skuIdToReduceAmountMap = orderInfo.computeOrderDetailPayAmount(orderInfo);

        orderInfoMapper.insert(orderInfo);

        // sku对应的订单明细 key = skuId ,value = orderDetail.id
        Map<Long, Long> skuIdToOrderDetailIdMap = new HashMap<>();

        //  赋值订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        if (!CollectionUtils.isEmpty(orderDetailList)){
            for (OrderDetail orderDetail : orderDetailList) {
                orderDetail.setOrderId(orderInfo.getId());
                orderDetail.setCreateTime(new Date());
                // 促销活动分摊金额
                BigDecimal splitActivityAmount = skuIdToReduceAmountMap.get("activity:" + orderDetail.getSkuId());
                //  判断
                if (splitActivityAmount==null){
                    //  如果这个值为null 给 0
                    splitActivityAmount = new BigDecimal(0);
                }
                orderDetail.setSplitActivityAmount(splitActivityAmount);
                //优惠券分摊金额
                BigDecimal splitCouponAmount = skuIdToReduceAmountMap.get("coupon:" + orderDetail.getSkuId());
                //  判断
                if (splitCouponAmount==null){
                    //  如果这个值为null 给 0
                    splitCouponAmount = new BigDecimal(0);
                }
                orderDetail.setSplitCouponAmount(splitCouponAmount);

                //优惠后的总金额
                BigDecimal skuTotalAmount = orderDetail.getOrderPrice().multiply(new BigDecimal(orderDetail.getSkuNum()));
                BigDecimal payAmount = skuTotalAmount.subtract(splitActivityAmount).subtract(splitCouponAmount);
                //  实际的付款金额
                orderDetail.setSplitTotalAmount(payAmount);
                //  插入订单明细
                orderDetailMapper.insert(orderDetail);
                //  赋值数据！
                skuIdToOrderDetailIdMap.put(orderDetail.getSkuId(),orderDetail.getId());

            }
        }
        //  做了一个数据的插入！ 业务，保存对应的order_detail_activity，order_detail_coupon
        this.saveActivityAndCouponRecord(orderInfo, skuIdToOrderDetailIdMap);

        //记录订单状态
        this.saveOrderStatusLog(orderInfo.getId(), orderInfo.getOrderStatus());

        //  返回的是订单Id 根据订单Id 进行支付
        return orderInfo.getId();
    }

    //  保存记录 order_detail_activity，order_detail_coupon
    @Transactional(rollbackFor = Exception.class)
    public void saveActivityAndCouponRecord(OrderInfo orderInfo, Map<Long, Long> skuIdToOrderDetailIdMap) {
        //  先获取订单明细
        List<OrderDetailVo> orderDetailVoList = orderInfo.getOrderDetailVoList();
        //  判断集合不为空
        if (!CollectionUtils.isEmpty(orderDetailVoList)){
            //  循环遍历
            for (OrderDetailVo orderDetailVo : orderDetailVoList) {
                ActivityRule activityRule = orderDetailVo.getActivityRule();
                if (activityRule!=null){
                    List<Long> skuIdList = activityRule.getSkuIdList();
                    if (!CollectionUtils.isEmpty(skuIdList)){
                        //  循环遍历
                        for (Long skuId : skuIdList) {
                            //  插入数据！
                            OrderDetailActivity orderDetailActivity = new OrderDetailActivity();
                            orderDetailActivity.setOrderId(orderInfo.getId());
                            orderDetailActivity.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                            orderDetailActivity.setActivityId(activityRule.getActivityId());
                            orderDetailActivity.setActivityRule(activityRule.getId());
                            orderDetailActivity.setSkuId(skuId);
                            orderDetailActivity.setCreateTime(new Date());
                            orderDetailActivityMapper.insert(orderDetailActivity);
                        }
                    }
                }
            }
        }

        //  优惠券：order_detail_coupon
        CouponInfo couponInfo = orderInfo.getCouponInfo();
        if (couponInfo!=null){
            List<Long> skuIdList = couponInfo.getSkuIdList();
            //  循环遍历
            if (!CollectionUtils.isEmpty(skuIdList)){
                for (Long skuId : skuIdList) {
                    OrderDetailCoupon orderDetailCoupon = new OrderDetailCoupon();
                    orderDetailCoupon.setOrderId(orderInfo.getId());
                    orderDetailCoupon.setOrderDetailId(skuIdToOrderDetailIdMap.get(skuId));
                    orderDetailCoupon.setCouponId(couponInfo.getId());
                    orderDetailCoupon.setSkuId(skuId);
                    orderDetailCoupon.setCreateTime(new Date());
                    orderDetailCouponMapper.insert(orderDetailCoupon);
                }
            }
            //  优惠券使用状态要更新！
            activityFeignClient.updateCouponInfoUseStatus(couponInfo.getId(),orderInfo.getUserId(),orderInfo.getId());
        }
    }

    //  保存订单状态记录！
    private void saveOrderStatusLog(Long orderId, String orderStatus) {
        OrderStatusLog orderStatusLog = new OrderStatusLog();
        orderStatusLog.setOrderId(orderId);
        orderStatusLog.setOrderStatus(orderStatus);
        orderStatusLog.setOperateTime(new Date());
        //  插入数据
        orderStatusLogMapper.insert(orderStatusLog);
    }

    @Override
    public String getTradeNo(String userId) {
        //  制作一个uuid
        String tradeNo = UUID.randomUUID().toString();
        //  存储到缓存中
        //  采用String 数据类型
        String tradeNoKey = "user:"+userId+":tradeNo";
        redisTemplate.opsForValue().set(tradeNoKey,tradeNo);
        //  返回流水号
        return tradeNo;
    }

    @Override
    public boolean checkTradeNo(String tradeNo, String userId) {

        //  缓存流水号与页面的流水号比较
        String tradeNoKey = "user:"+userId+":tradeNo";
        //  缓存的流水号
        String redisTradeNo = (String) redisTemplate.opsForValue().get(tradeNoKey);
        //  直接返回比较结果
        return tradeNo.equals(redisTradeNo);
    }


    @Override
    public void delTradeNo(String userId) {
        //  获取缓存的key
        String tradeNoKey = "user:"+userId+":tradeNo";
        //  直接删除
        redisTemplate.delete(tradeNoKey);
    }

    @Override
    public boolean checkStock(Long skuId, Integer skuNum) {
        //  远程调用：能否使用feign 远程调用? 不行！ httpClient
        //  http://localhost:9001/hasStock?skuId=10221&num=2
        String result = HttpClientUtil.doGet(wareUrl + "/hasStock?skuId=" + skuId + "&num=" + skuNum);
        //  0：无库存   1：有库存

        return "1".equals(result);
    }

    @Override
    public void execExpiredOrder(Long orderId) {
        //  要操作更新语句  update order_info set order_status=? , process_status = ? where id = orderId;
        //        OrderInfo orderInfo = new OrderInfo();
        //        orderInfo.setId(orderId);
        //        orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        //        orderInfo.setProcessStatus(ProcessStatus.CLOSED.name());
        //        orderInfoMapper.updateById(orderInfo);

        //  后续我们会有很多类似的操作 ： 根据订单Id 修改订单的状态，还有修改进度的状态
        //  例如：以后我们会讲到拆单： updateOrderStatus(orderId,ProcessStatus.SPLIT);
        updateOrderStatus(orderId,ProcessStatus.CLOSED);

        //  如果有交易记录，那么我们需要关闭交易记录，发送消息即可！
        rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
    }
    //  根据订单Id ，已经进度状态更新订单表
    public void updateOrderStatus(Long orderId, ProcessStatus processStatus) {
        OrderInfo orderInfo = new OrderInfo();
        orderInfo.setId(orderId);
        //  订单的状态
        //  orderInfo.setOrderStatus(OrderStatus.CLOSED.name());
        orderInfo.setOrderStatus(processStatus.getOrderStatus().name());
        orderInfo.setProcessStatus(processStatus.name());
        orderInfoMapper.updateById(orderInfo);
    }

    @Override
    public OrderInfo getOrderInfo(Long orderId) {
        //  查询订单信息+订单明细信息
        OrderInfo orderInfo = orderInfoMapper.selectById(orderId);
        if (orderInfo!=null){
            //  获取订单明细数据
            List<OrderDetail> orderDetailList = orderDetailMapper.selectList(new QueryWrapper<OrderDetail>().eq("order_id", orderId));

            //  赋值
            orderInfo.setOrderDetailList(orderDetailList);
        }
        //  返回orderInfo 对象
        return orderInfo;
    }

    @Override
    public void sendOrderStatus(Long orderId) {
        //  更改一个订单的状态：
        this.updateOrderStatus(orderId,ProcessStatus.NOTIFIED_WARE);
        //  接收当前发送的消息
        String wareJson = this.initWareOrder(orderId);
        //  发送消息：
        this.rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_WARE_STOCK,MqConst.ROUTING_WARE_STOCK,wareJson);
    }
    //  获取发送的Json 字符串
    private String initWareOrder(Long orderId) {
        //  这Json 字符串是由OrderInfo 构成的。 先获取到orderInfo
        OrderInfo orderInfo = this.getOrderInfo(orderId);
        //  将orderInfo 中对应的字段 转成一个 Map 集合,这个map 中只有我们需要的这几个字段值。
        Map map = this.initWareOrder(orderInfo);
        //  将这个有效的map 集合转换Json 字符串
        return JSON.toJSONString(map);
    }

    //  这个方法就是将OrderInfo 中的部分字段转换为map 集合。 后面会使用到这个方法。
    public Map initWareOrder(OrderInfo orderInfo) {
        HashMap<String, Object> map = new HashMap<>();
        //  封装需要的使用的字段即可
        map.put("orderId",orderInfo.getId());
        map.put("consignee",orderInfo.getConsignee());
        map.put("consigneeTel",orderInfo.getConsigneeTel());
        map.put("orderComment",orderInfo.getOrderComment());
        map.put("orderBody",orderInfo.getTradeBody());
        map.put("deliveryAddress",orderInfo.getDeliveryAddress());
        map.put("paymentWay","2"); // 在线付款
        map.put("wareId",orderInfo.getWareId());    // 设置仓库Id
        //  赋值订单明细
        List<OrderDetail> orderDetailList = orderInfo.getOrderDetailList();
        List<Map> maps = new ArrayList<>();
        /*
        details:[{skuId:101,skuNum:1,skuName:’小米手64G’},
                 {skuId:201,skuNum:1,skuName:’索尼耳机’}]
         */
        if (!CollectionUtils.isEmpty(orderDetailList)){
            //  循环获取数据
            for (OrderDetail orderDetail : orderDetailList) {
                //  声明一个Map 集合
                HashMap<Object, Object> hashMap = new HashMap<>();
                hashMap.put("skuId",orderDetail.getSkuId());
                hashMap.put("skuNum",orderDetail.getSkuNum());
                hashMap.put("skuName",orderDetail.getSkuName());

                //  将hashMap 添加到集合中
                maps.add(hashMap);
            }
        }
        map.put("details",maps);
        return map;
    }

    //  拆单实现类，最终要获取到的是子订单集合
    //  如果有优惠券+活动了，那么订单明细就需要计算出实际付款金额！
    @Override
    public List<OrderInfo> orderSplit(String orderId, String wareSkuMap) {
        ArrayList<OrderInfo> orderInfoArrayList = new ArrayList<>();
    /*
    1.  先获取到原始订单 107
    2.  将w bareSkuMap 转换为我们能操作的对象 [{"wareId":"1","skuIds":["2","10"]},{"wareId":"2","skuIds":["3"]}]
        方案一：class Param{
                    private String wareId;
                    private List<String> skuIds;
                }
        方案二：看做一个Map mpa.put("wareId",value); map.put("skuIds",value)

    3.  创建一个新的子订单 108 109 。。。
    4.  给子订单赋值
    5.  保存子订单到数据库
    6.  修改原始订单的状态
    7.  测试
     */
        OrderInfo orderInfoOrigin = getOrderInfo(Long.parseLong(orderId));
        List<Map> maps = JSON.parseArray(wareSkuMap, Map.class);
        if (maps != null) {
            for (Map map : maps) {
                String wareId = (String) map.get("wareId");

                List<String> skuIds = (List<String>) map.get("skuIds");

                OrderInfo subOrderInfo = new OrderInfo();
                // 属性拷贝
                BeanUtils.copyProperties(orderInfoOrigin, subOrderInfo);
                // 防止主键冲突
                subOrderInfo.setId(null);
                subOrderInfo.setParentOrderId(Long.parseLong(orderId));
                // 赋值仓库Id
                subOrderInfo.setWareId(wareId);
                // 子订单号
                String outTradeNo = "ATGUIGU" + System.currentTimeMillis() + "" + new Random().nextInt(1000);
                subOrderInfo.setOutTradeNo(outTradeNo);

                // 计算子订单的金额: 必须有订单明细
                // 获取到子订单明细
                // 声明一个集合来存储子订单明细
                ArrayList<OrderDetail> subOrderDetailList = new ArrayList<>();

                List<OrderDetail> orderDetailList = orderInfoOrigin.getOrderDetailList();
                // 表示主主订单明细中获取到子订单的明细
                if (orderDetailList != null && orderDetailList.size() > 0) {
                    for (OrderDetail orderDetail : orderDetailList) {
                        // 获取子订单明细的商品Id
                        for (String skuId : skuIds) {
                            if (Long.parseLong(skuId) == orderDetail.getSkuId().longValue()) {
                                OrderDetail subOrderDetail = new OrderDetail();
                                BeanUtils.copyProperties(orderDetail, subOrderDetail);
                                subOrderDetail.setId(null);
                                // 将订单明细添加到集合
                                subOrderDetailList.add(subOrderDetail);
                            }
                        }
                    }
                }
                subOrderInfo.setOrderDetailList(subOrderDetailList);

                // 重新计算子订单和订单明细金额
                BigDecimal totalAmount = new BigDecimal("0");
                BigDecimal originalTotalAmount = new BigDecimal("0");
                BigDecimal couponAmount = new BigDecimal("0");
                BigDecimal activityReduceAmount = new BigDecimal("0");
                for (OrderDetail subOrderDetail : subOrderDetailList) {
                    BigDecimal skuTotalAmount = subOrderDetail.getOrderPrice().multiply(new BigDecimal(subOrderDetail.getSkuNum()));
                    originalTotalAmount = originalTotalAmount.add(skuTotalAmount);
                    totalAmount = totalAmount.add(skuTotalAmount).subtract(subOrderDetail.getSplitCouponAmount()).subtract(subOrderDetail.getSplitActivityAmount());
                    couponAmount = couponAmount.add(subOrderDetail.getSplitCouponAmount());
                    activityReduceAmount = activityReduceAmount.add(subOrderDetail.getSplitActivityAmount());
                }
                subOrderInfo.setTotalAmount(totalAmount);
                subOrderInfo.setOriginalTotalAmount(originalTotalAmount);
                subOrderInfo.setCouponAmount(couponAmount);
                subOrderInfo.setActivityReduceAmount(activityReduceAmount);
                subOrderInfo.setFeightFee(new BigDecimal(0));
                orderInfoMapper.insert(subOrderInfo);

                //保存子订单明细
                for (OrderDetail subOrderDetail : subOrderDetailList) {
                    subOrderDetail.setOrderId(subOrderInfo.getId());
                    orderDetailMapper.insert(subOrderDetail);
                }

                // 保存订单状态记录
                List<OrderStatusLog> orderStatusLogList = orderStatusLogMapper.selectList(new QueryWrapper<OrderStatusLog>().eq("order_id", orderId));
                for(OrderStatusLog orderStatusLog : orderStatusLogList) {
                    OrderStatusLog subOrderStatusLog = new OrderStatusLog();
                    BeanUtils.copyProperties(orderStatusLog, subOrderStatusLog);
                    subOrderStatusLog.setId(null);
                    subOrderStatusLog.setOrderId(subOrderInfo.getId());
                    orderStatusLogMapper.insert(subOrderStatusLog);
                }
                // 将子订单添加到集合中！
                orderInfoArrayList.add(subOrderInfo);
            }
        }
        // 修改原始订单的状态
        updateOrderStatus(Long.parseLong(orderId), ProcessStatus.SPLIT);
        return orderInfoArrayList;
    }

    @Override
    public void execExpiredOrder(Long orderId, String flag) {
        //  这个会关闭orderInfo;
        updateOrderStatus(orderId,ProcessStatus.CLOSED);
        //  判断flag
        if ("2".equals(flag)){
            //  需要关闭两个表 orderInfo,paymentInfo
            //  如果有交易记录，那么我们需要关闭交易记录，发送消息即可！
            rabbitService.sendMessage(MqConst.EXCHANGE_DIRECT_PAYMENT_CLOSE,MqConst.ROUTING_PAYMENT_CLOSE,orderId);
        }
    }
}
